package com.engine.salary.service.impl;

import com.cloudstore.dev.api.util.Util_DataCache;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.biz.*;
import com.engine.salary.common.LocalDateRange;
import com.engine.salary.component.WeaTableColumnGroup;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.salary.constant.SalaryDefaultTenantConstant;
import com.engine.salary.constant.SalaryItemConstant;
import com.engine.salary.entity.datacollection.AddUpSituation;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.salaryarchive.bo.SalaryArchiveBO;
import com.engine.salary.entity.salaryarchive.dto.SalaryArchiveDataDTO;
import com.engine.salary.entity.salaryarchive.dto.SalaryArchiveListDTO;
import com.engine.salary.entity.salaryarchive.param.*;
import com.engine.salary.entity.salaryarchive.po.SalaryArchiveDimissionPO;
import com.engine.salary.entity.salaryarchive.po.SalaryArchiveItemPO;
import com.engine.salary.entity.salaryarchive.po.SalaryArchivePO;
import com.engine.salary.entity.salaryarchive.po.SalaryArchiveTaxAgentPO;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.taxagent.dto.TaxAgentManageRangeEmployeeDTO;
import com.engine.salary.entity.taxagent.param.TaxAgentQueryParam;
import com.engine.salary.entity.taxagent.po.TaxAgentEmpChangePO;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.OperateTypeEnum;
import com.engine.salary.enums.UserStatusEnum;
import com.engine.salary.enums.auth.AuthFilterTypeEnum;
import com.engine.salary.enums.datacollection.UseEmployeeTypeEnum;
import com.engine.salary.enums.salaryarchive.*;
import com.engine.salary.enums.salaryitem.SalaryDataTypeEnum;
import com.engine.salary.enums.taxagent.TaxAgentEmpChangeModuleEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.archive.SalaryArchiveItemMapper;
import com.engine.salary.mapper.archive.SalaryArchiveMapper;
import com.engine.salary.service.*;
import com.engine.salary.service.auth.AuthService;
import com.engine.salary.service.auth.AuthServiceImpl;
import com.engine.salary.sys.constant.SalarySysConstant;
import com.engine.salary.sys.entity.po.SalarySysConfPO;
import com.engine.salary.sys.entity.vo.OrderRuleVO;
import com.engine.salary.sys.enums.OpenEnum;
import com.engine.salary.sys.enums.SalaryAcctEmployeeRuleEnum;
import com.engine.salary.sys.service.SalarySysConfService;
import com.engine.salary.sys.service.impl.SalarySysConfServiceImpl;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.IdGenerator;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.excel.ExcelParseHelper;
import com.engine.salary.util.excel.ExcelSupport;
import com.engine.salary.util.excel.ExcelUtilPlus;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.engine.salary.util.valid.ValidUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import weaver.file.ImageFileManager;
import weaver.general.Util;
import weaver.hrm.User;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.engine.salary.sys.constant.SalarySysConstant.OPEN_SECONDARY_ACCOUNT;
import static com.engine.salary.util.excel.ExcelSupport.EXCEL_TYPE_XLSX;

/**
 * 薪资档案
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
@Slf4j
public class SalaryArchiveServiceImpl extends Service implements SalaryArchiveService {

    private SalaryArchiveBiz salaryArchiveMapper = new SalaryArchiveBiz();
    private SalaryArchiveItemBiz salaryArchiveItemMapper = new SalaryArchiveItemBiz();
    private SalaryArchiveTaxAgentBiz salaryArchiveTaxAgentMapper = new SalaryArchiveTaxAgentBiz();
    private SalaryArchiveDimissionBiz salaryArchiveDimissionMapper = new SalaryArchiveDimissionBiz();
    private SalaryItemBiz salaryItemMapper = new SalaryItemBiz();


    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }

    private SalaryArchiveMapper getSalaryArchiveMapper() {
        return MapperProxyFactory.getProxy(SalaryArchiveMapper.class);
    }

    private SalaryArchiveItemMapper getSalaryArchiveItemMapper() {
        return MapperProxyFactory.getProxy(SalaryArchiveItemMapper.class);
    }

    private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) {
        return ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user);
    }

    private SalarySysConfService getSalarySysConfService(User user) {
        return ServiceUtil.getService(SalarySysConfServiceImpl.class, user);
    }

    private SalaryEmployeeService getSalaryEmployeeService(User user) {
        return ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user);
    }

    private TaxAgentEmpChangeService getTaxAgentEmpChangeService(User user) {
        return ServiceUtil.getService(TaxAgentEmpChangeServiceImpl.class, user);
    }

    private SalaryArchiveItemService getSalaryArchiveItemService(User user) {
        return ServiceUtil.getService(SalaryArchiveItemServiceImpl.class, user);
    }

    public SalarySobService getSalarySobService(User user) {
        return ServiceUtil.getService(SalarySobServiceImpl.class, user);
    }

    public AuthService getAuthService(User user) {
        return ServiceUtil.getService(AuthServiceImpl.class, user);
    }

    //主次账号是否开启
    boolean openSecondaryAccount = "1".equals(getSalarySysConfService(user).getValueByCode(OPEN_SECONDARY_ACCOUNT));

    @Override
    public SalaryArchivePO getById(Long salaryArchiveId) {
        return salaryArchiveMapper.getById(salaryArchiveId);
    }

    @Override
    public List<SalaryArchivePO> listSome(SalaryArchivePO po) {
        Collection<Long> ids = po.getIds();
        if (CollectionUtils.isNotEmpty(ids)) {
            List<SalaryArchivePO> list = new ArrayList<>();
            List<List<Long>> partition = Lists.partition((List<Long>) ids, 1000);
            partition.forEach(idss -> {
                po.setIds(idss);
                list.addAll(getSalaryArchiveMapper().listSome(po));
            });
            return list;
        }

        Collection<Long> employeeIds = po.getEmployeeIds();
        if (CollectionUtils.isNotEmpty(employeeIds)) {
            List<SalaryArchivePO> list = new ArrayList<>();
            List<List<Long>> partition = Lists.partition((List<Long>) employeeIds, 1000);
            partition.forEach(emps -> {
                po.setEmployeeIds(emps);
                list.addAll(getSalaryArchiveMapper().listSome(po));
            });
            return list;
        }

        // 获取薪资档案数据
        return getSalaryArchiveMapper().listSome(po);
    }

    @Override
    public List<SalaryArchiveListDTO> getSalaryArchiveList(SalaryArchiveQueryParam queryParam) {
        if (queryParam.isExtSalaryArchiveList()) {
            return getSalaryArchiveMapper().listExtSalaryArchive(queryParam);
        }
        if (StringUtils.isNotBlank(queryParam.getPayStartDateStartDateStr())) {
            queryParam.setPayStartDateStartDate(SalaryDateUtil.stringToDate(queryParam.getPayStartDateStartDateStr()));
        }
        if (Objects.nonNull(queryParam.getPayStartDateEndDateStr())) {
            queryParam.setPayStartDateEndDate(SalaryDateUtil.stringToDate(queryParam.getPayStartDateEndDateStr()));
        }

        if (StringUtils.isNotBlank(queryParam.getPayEndDateStartDateStr())) {
            queryParam.setPayEndDateStartDate(SalaryDateUtil.stringToDate(queryParam.getPayEndDateStartDateStr()));
        }
        if (Objects.nonNull(queryParam.getPayEndDateEndDateStr())) {
            queryParam.setPayEndDateEndDate(SalaryDateUtil.stringToDate(queryParam.getPayEndDateEndDateStr()));
        }
        List<SalaryArchiveListDTO> list = getSalaryArchiveMapper().list(queryParam);

        // 过滤调薪日期
        if (queryParam.getAdjustSalaryStartDate() != null || queryParam.getAdjustSalaryEndDate() != null) {
            List<Long> salaryArchiveIds = list.stream().map(SalaryArchiveListDTO::getId).collect(Collectors.toList());
            LocalDateRange dateRange = LocalDateRange.builder().fromDate(queryParam.getAdjustSalaryStartDate()).endDate(queryParam.getAdjustSalaryEndDate()).build();
            List<SalaryArchiveItemPO> salaryArchiveItemPOList = getSalaryArchiveItemService(user).listByArchiveIdAndEffectiveTime(salaryArchiveIds, dateRange);
            List<Long> archiveIds = salaryArchiveItemPOList.stream().map(SalaryArchiveItemPO::getSalaryArchiveId).distinct().collect(Collectors.toList());
            list = list.stream().filter(dto -> archiveIds.contains(dto.getId())).collect(Collectors.toList());
        }

        if (!openSecondaryAccount) {
            list = list.stream().filter(e -> e.getAccountType() == null || e.getAccountType() == 0).collect(Collectors.toList());
        }
        return SalaryI18nUtil.i18nList(list);
    }

    @Override
    public PageInfo<SalaryArchiveListDTO> listPage(SalaryArchiveQueryParam queryParam) {
        long currentEmployeeId = user.getUID();

        /**
         * 异步处理档案数据
         */
        String handleable = Util.null2String(Util_DataCache.getObjVal("salaryArchiveHandleable"));
        if ((StringUtils.isBlank(handleable) || OpenEnum.OPEN.getValue().equals(handleable)) && !queryParam.isExtSalaryArchiveList()) {
            new Thread() {
                public void run() {
                    Util_DataCache.setObjVal("salaryArchiveHandleable", "0");
                    // 1.历史数据处理
                    handleHistory(currentEmployeeId);
                    // 2.待停薪自动处理
                    handleSuspendData(currentEmployeeId);
                    // 3.增量数据处理
//                    handleChangeData(currentEmployeeId);
                    Util_DataCache.setObjVal("salaryArchiveHandleable", "1");
                }
            }.start();
        }

        //排序配置
        OrderRuleVO orderRule = getSalarySysConfService(user).orderRule();
        queryParam.setOrderRule(orderRule);
        List<SalaryArchiveListDTO> list = getSalaryArchiveList(queryParam);
        list = list.stream()
                //过滤档案状态
                .filter(dto -> {
                    if (StringUtils.isNotBlank(queryParam.getArchiveStatus())) {
                        return StringUtils.equals(queryParam.getArchiveStatus(), dto.getArchiveStatus());
                    }
                    return true;
                })
                .collect(Collectors.toList());

        //能查看哪些档案
        list = getAuthService(user).auth(list, AuthFilterTypeEnum.DATA_OPT , SalaryArchiveListDTO.class);


        PageInfo<SalaryArchiveListDTO> pageInfo = SalaryPageUtil.buildPage(queryParam.getCurrent(), queryParam.getPageSize(), SalaryArchiveListDTO.class);
        pageInfo.setTotal(list.size());
        pageInfo.setList(SalaryPageUtil.subList(queryParam.getCurrent(), queryParam.getPageSize(), list));
        return pageInfo;
    }

    /**
     * 1、员工的人事状态属性从在职变成非在职，且在【发薪员工】里，则自动进入【待停薪】；
     * 2、若维护了最后发薪日大于当前日期，会自动清除该员工的待办数据；
     * 3、个税扣缴义务人发生调整的发薪员工自动进入【待停薪】；
     * <p>
     * 1.【发薪员工】中，员工的人事状态属性从在职变成非在职，则自动进入【待停薪】；
     * 2.【定薪员工】中，如果维护了最后发薪日且大于今天的，自动清除待停薪，如果小于等于今天的则自动进入待停薪
     *
     * @param currentEmployeeId
     */
    @Transactional(rollbackFor = Exception.class)
    public void handleSuspendData(Long currentEmployeeId) {
        List<String> personnelStatuss = new ArrayList<String>() {{
            add("4");
            add("5");
            add("6");
        }};

        // 1.定薪员工非在职
        if (CollectionUtils.isNotEmpty(personnelStatuss)) {
            SalaryArchiveQueryParam queryParam = SalaryArchiveQueryParam.builder()
                    // 离职
                    .personnelStatuss(personnelStatuss)
                    // 定薪列表
                    .runStatusList(Collections.singletonList(SalaryArchiveStatusEnum.FIXED.getValue()))
                    .build();
            List<SalaryArchiveListDTO> noNormalList = getSalaryArchiveList(queryParam);
            if (CollectionUtils.isNotEmpty(noNormalList)) {
                List<Long> idList = noNormalList.stream().map(SalaryArchiveListDTO::getId).collect(Collectors.toList());
                List<List<Long>> partition = Lists.partition(idList, 999);
                partition.forEach(part ->
                        getSalaryArchiveMapper().updateRunStatusByIdsAndPayEndDate(SalaryArchivePO.builder().ids(part).runStatus(SalaryArchiveStatusEnum.SUSPEND.getValue()).build())
                );
            }
        }
        Date today = new Date();
        // 2.定薪员工维护了最后发薪日,且大于今天的，自动清除待停薪
        getSalaryArchiveMapper().updateFixed(SalaryArchivePO.builder().payEndDate(today).build());
        // 3.定薪员工维护了最后发薪日，且小于等于今天的，自动进入待停薪
        getSalaryArchiveMapper().updateSuspend(SalaryArchivePO.builder().payEndDate(today).build());
    }


    /**
     * 处理增量数据
     *
     * @param currentEmployeeId
     */
    public void handleChangeData(Long currentEmployeeId) {

        // 所有增量人员列表
        List<TaxAgentEmpChangePO> taxAgentEmpChangeList = getTaxAgentEmpChangeService(user).listAllByModule(TaxAgentEmpChangeModuleEnum.SALARY_ARCHIVE);
        if (CollectionUtils.isEmpty(taxAgentEmpChangeList)) {
            return;
        }
        // 当前可以管辖的人员
        if (currentEmployeeId != 1L) {
            TaxAgentQueryParam param = TaxAgentQueryParam.builder().build();
            param.setFilterType(AuthFilterTypeEnum.ADMIN_DATA);
            Collection<TaxAgentPO> taxAgentList =getTaxAgentService(user).listAuth(param);
            taxAgentEmpChangeList = taxAgentEmpChangeList.stream().filter(f -> taxAgentList.stream().anyMatch(e -> e.getId().equals(f.getTaxAgentId()))).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(taxAgentEmpChangeList)) {
                return;
            }
        }
        // 所有档案数据
        List<SalaryArchivePO> salaryArchiveList = getSalaryArchiveMapper().listAll();
        // 获取所有可被引用的薪资项目
        List<SalaryItemPO> salaryItems = salaryItemMapper.getCanAdjustSalaryItems();
        Collection<Long> salaryItemIds = salaryItems.stream().map(SalaryItemPO::getId).collect(Collectors.toList());
        // 获取薪资档案所对应的当前生效的薪资项目数据
        List<SalaryArchiveItemPO> salaryArchiveItemList = getCurrentEffectiveItemList(Lists.newArrayList(), salaryItemIds);
        /** 增量处理 start ***/
        SalaryArchiveBO.ChangeData changeData = SalaryArchiveBO.buildChangeData(taxAgentEmpChangeList, salaryArchiveList, salaryArchiveItemList, currentEmployeeId);
        // 批量修改薪资档案
        if (CollectionUtils.isNotEmpty(changeData.getSalaryArchiveUpdateTodoList())) {
            salaryArchiveMapper.batchUpdate(changeData.getSalaryArchiveUpdateTodoList());
        }
        // 批量新增薪资档案
        if (CollectionUtils.isNotEmpty(changeData.getSalaryArchiveAddTodoList())) {
            salaryArchiveMapper.batchInsert(changeData.getSalaryArchiveAddTodoList());
        }
        // 落库处理薪资项目
        if (CollectionUtils.isNotEmpty(changeData.getSalaryArchiveItemAddTodos())) {
            salaryArchiveItemMapper.batchInsert(changeData.getSalaryArchiveItemAddTodos(), user);
        }
        // 删除增量数据
        if (CollectionUtils.isNotEmpty(changeData.getChangeIds())) {
            getTaxAgentEmpChangeService(user).deleleByIds(changeData.getChangeIds());
        }
        /** 增量处理 end ***/

    }

    @Override
    public void deleteSalaryArchive(Collection<Long> salaryArchiveIds) {
        if (CollectionUtils.isEmpty(salaryArchiveIds)) {
            throw new SalaryRunTimeException("薪资档案参数为空!");
        }
        SalarySysConfPO canDelete = getSalarySysConfService(user).getOneByCode(SalarySysConstant.SALARY_ARCHIVE_DELETE);
        if (Objects.isNull(canDelete) || StringUtils.equals(canDelete.getConfValue(), "0")) {
            throw new SalaryRunTimeException("不允许删除薪资档案，请先开启删除档案规则配置！");
        }
        List<SalaryArchivePO> salaryArchiveList = getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().ids(salaryArchiveIds).build());
        if (CollectionUtils.isEmpty(salaryArchiveList)) {
            throw new SalaryRunTimeException("薪资档案不存在");
        }
        //权限，可操作的档案
        salaryArchiveList = getAuthService(user).auth(salaryArchiveList, AuthFilterTypeEnum.ADMIN_DATA, SalaryArchivePO.class);
        if (CollectionUtils.isEmpty(salaryArchiveList)) {
            throw new SalaryRunTimeException("没有权限删除该薪资档案！");
        }
        Optional<SalaryArchivePO> fixedList = salaryArchiveList.stream().filter(archive -> !StringUtils.equals(archive.getRunStatus(), SalaryArchiveStatusEnum.PENDING.getValue())
                && !StringUtils.equals(archive.getRunStatus(), SalaryArchiveStatusEnum.STOP_FROM_PENDING.getValue())).findFirst();
        if (fixedList.isPresent()) {
            throw new SalaryRunTimeException("发薪员工、待停薪员工、停薪_来自待停薪，无法删除薪资档案！");
        }
        List<Long> deleteIds = salaryArchiveList.stream().map(SalaryArchivePO::getId).collect(Collectors.toList());
        // 删除薪资档案及档案项目
        if (CollectionUtils.isNotEmpty(deleteIds)) {
            getSalaryArchiveMapper().deleteByIds(deleteIds);
            getSalaryArchiveItemMapper().deleteBySalaryArchiveId(deleteIds);
        }
    }


    @Override
    public List<SalaryArchiveListDTO> list(SalaryArchiveQueryParam queryParam) {
        //排序配置
        OrderRuleVO orderRule = getSalarySysConfService(user).orderRule();
        queryParam.setOrderRule(orderRule);

        List<SalaryArchiveListDTO> list = getSalaryArchiveList(queryParam);
        return getAuthService(user).auth(list, queryParam.getFilterType(), SalaryArchiveListDTO.class);
    }

    /**
     * 获取薪资档案对应的当前生效的薪资项目
     *
     * @param salaryArchivesIds
     * @return
     */
    @Override
    public List<SalaryArchiveItemPO> getCurrentEffectiveItemList(Collection<Long> salaryArchivesIds, Collection<Long> salaryItemIds) {
        if (CollectionUtils.isEmpty(salaryItemIds)) {
            return Collections.EMPTY_LIST;
        }
        List<SalaryArchiveItemPO> salaryArchiveItems = salaryArchiveItemMapper.getCurrentEffectiveItemList(SalaryArchiveItemQueryParam.builder().salaryArchivesIds(salaryArchivesIds).salaryItemIds(salaryItemIds).effectiveTime(new Date()).build());
        return salaryArchiveItems.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(f -> f.getSalaryArchiveId() + "-" + f.getSalaryItemId()))), ArrayList::new));
    }

    /**
     * 获取薪资档案对应的当前生效的个税扣缴义务人
     *
     * @param salaryArchivesIds
     * @return
     */
    @Override
    public List<SalaryArchiveTaxAgentPO> getCurrentEffectiveTaxAgentList(Collection<Long> salaryArchivesIds) {
        if (CollectionUtils.isEmpty(salaryArchivesIds)) {
            return Collections.EMPTY_LIST;
        }
        List<SalaryArchiveTaxAgentPO> salaryArchiveTaxAgents = salaryArchiveTaxAgentMapper.listByParam(SalaryArchiveTaxAgentQueryParam.builder().salaryArchivesIds(salaryArchivesIds).effectiveTime(new Date()).build());

        return salaryArchiveTaxAgents.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(SalaryArchiveTaxAgentPO::getSalaryArchiveId))), ArrayList::new));
    }

    /**
     * 构建薪资档案数据
     *
     * @param salaryArchives
     * @param taxAgentLists
     * @param salaryItems
     * @param isPage
     * @return
     */
    @Override
    public List<Map<String, Object>> buildSalaryArchiveData(Collection<SalaryArchiveListDTO> salaryArchives, Collection<TaxAgentPO> taxAgentLists, List<SalaryItemPO> salaryItems, Boolean isPage) {
        // 分页用于表格展示，否则用于导出
        Collection<Long> ids = isPage ? salaryArchives.stream().map(SalaryArchiveListDTO::getId).collect(Collectors.toList()) : CollectionUtils.emptyCollection();

        List<Long> salaryItemIds = salaryItems.stream().map(SalaryItemPO::getId).collect(Collectors.toList());
        // 1.获取薪资档案所对应的当前生效的薪资项目数据
        List<SalaryArchiveItemPO> salaryArchiveItemList = Collections.emptyList();
        if (CollectionUtils.isNotEmpty(ids) && CollectionUtils.isNotEmpty(salaryItemIds) || !isPage) {
            salaryArchiveItemList = getCurrentEffectiveItemList(ids, salaryItemIds);
        }
        List<SalaryArchiveItemPO> finalSalaryArchiveItemList = salaryArchiveItemList;
        List<Map<String, Object>> salaryArchiveItemData = salaryArchives.stream().map(m -> {
            Map<String, Object> map = Maps.newHashMap();
            map.put("salaryArchiveId", m.getId());
            map.put("archiveStatus", m.getArchiveStatus());
            List<SalaryArchiveItemPO> salaryArchiveItemValuelList = finalSalaryArchiveItemList.stream().filter(i -> i.getSalaryArchiveId().equals(m.getId())).collect(Collectors.toList());
            salaryArchiveItemValuelList.forEach(i -> {
                map.put(i.getSalaryItemId() + SalaryItemConstant.DYNAMIC_SUFFIX, i.getItemValue());
            });
            return map;
        }).collect(Collectors.toList());
        //2.获取薪资档案所对应的当前生效的个税扣缴义务人列表
//        List<SalaryArchiveTaxAgentPO> taxAgentList = getCurrentEffectiveTaxAgentList(ids);
//        List<Map<String, Object>> taxAgentData = taxAgentList.stream().map(m -> {
//            Map<String, Object> map = Maps.newHashMap();
//            map.put("salaryArchiveId", m.getSalaryArchiveId());
//            map.put("taxAgentId", m.getTaxAgentId());
//            Optional<TaxAgentPO> optional = taxAgentLists.stream().filter(f -> f.getId().equals(m.getTaxAgentId())).findFirst();
//            map.put("taxAgentName", optional.isPresent() ? optional.get().getName() : "");
//            map.put("taxAgentEffectiveTime", m.getEffectiveTime());
//            return map;
//        }).collect(Collectors.toList());
        Map<Long, TaxAgentPO> longTaxAgentPOMap = SalaryEntityUtil.convert2Map(taxAgentLists, TaxAgentPO::getId);
        // 3.组装数据
        List<Map<String, Object>> listMaps = new ArrayList<>();
        salaryArchives.forEach(e -> {
            e.setEmployeeStatus(NumberUtils.isCreatable(e.getEmployeeStatus()) ? UserStatusEnum.getDefaultLabelByValue(Integer.parseInt(e.getEmployeeStatus())) : "");

            Map<String, Object> map = new LinkedHashMap<>();
            map.put("id", e.getId());
            map.put("username", e.getUsername());
            map.put("employeeId", e.getEmployeeId());
            if (openSecondaryAccount) {
                map.put("accountType", e.getAccountType() == null || e.getAccountType() == 0 ? "主账号" : "次账号");
            }

//            Optional<Map<String, Object>> optionalTaxAgent = taxAgentData.stream().filter(f -> f.get("salaryArchiveId").toString().equals(e.getId().toString())).findFirst();
            TaxAgentPO taxAgentPO = longTaxAgentPOMap.get(e.getTaxAgentId());
            map.put("taxAgentName", Optional.ofNullable(taxAgentPO).map(TaxAgentPO::getName).orElse(""));
            map.put("taxAgentId", Optional.ofNullable(taxAgentPO).map(TaxAgentPO::getId).orElse(0L));
//            map.put("taxAgentEffectiveTime", optionalTaxAgent.isPresent() ? optionalTaxAgent.get().get("taxAgentEffectiveTime") : "");

            map.put("subcompanyName", e.getSubcompanyName());
            map.put("departmentName", e.getDepartmentName());
            map.put("mobile", e.getMobile());
            map.put("workcode", e.getWorkcode());
            map.put("idNo", e.getIdNo());
            map.put("employeeStatus", e.getEmployeeStatus());
            map.put("payStartDate", SalaryDateUtil.getFormatLocalDate(e.getPayStartDate()));
            map.put("payEndDate", SalaryDateUtil.getFormatLocalDate(e.getPayEndDate()));
            map.put("companystartdate", e.getCompanystartdate());
            map.put("dismissdate", e.getDismissdate());
            map.put("opts", e.getOpts());

            // 薪资项目动态
            Optional<Map<String, Object>> optionalItem = salaryArchiveItemData.stream().filter(f -> f.get("salaryArchiveId").toString().equals(e.getId().toString())).findFirst();
            optionalItem.ifPresent(map::putAll);

            listMaps.add(map);
        });
        return listMaps;
    }

    @Override
    public XSSFWorkbook exportList(SalaryArchiveQueryParam queryParam) {
        long employeeId = user.getUID();

        // 1.工作簿名称
        String sheetName = SalaryI18nUtil.getI18nLabel(85368, "薪资档案");
        // 获取所有可被引用的薪资项目
        List<SalaryItemPO> salaryItems = salaryItemMapper.getCanAdjustSalaryItems();
        Object[] header = {
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(85429, "姓名"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(86185, "部门"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(86176, "工号"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(86186, "手机号"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(86186, "证件号码"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(15890, "员工状态"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(91075, "起始发薪日期"), "", "", 0),
                new WeaTableColumnGroup("150px", SalaryI18nUtil.getI18nLabel(91075, "最后发薪日期"), "", "", 0)
        };
        // 2.表头
        List<Object> headerList = new ArrayList<>(Arrays.asList(header));
        for (SalaryItemPO salaryItem : salaryItems) {
            headerList.add(new WeaTableColumnGroup("150px", salaryItem.getName(), "", "", salaryItem.getPattern()));
        }

        // 获取所有个税扣缴义务人
        Collection<TaxAgentPO> taxAgentList = getTaxAgentService(user).listAll();
        //获取管理的人员范围
        List<TaxAgentManageRangeEmployeeDTO> taxAgentEmployeeDTOS = getTaxAgentService(user).listTaxAgentAndEmployeeTree(employeeId);
        Map<Long, List<TaxAgentManageRangeEmployeeDTO.TaxAgentEmployee>> taxAgentEmployeesMap = SalaryEntityUtil.convert2Map(taxAgentEmployeeDTOS, TaxAgentManageRangeEmployeeDTO::getTaxAgentId, TaxAgentManageRangeEmployeeDTO::getEmployeeList);


        //排序配置
        OrderRuleVO orderRule = getSalarySysConfService(user).orderRule();
        queryParam.setOrderRule(orderRule);

        List<SalaryArchiveListDTO> salaryArchives = getSalaryArchiveList(queryParam);

        //分权
        salaryArchives = getAuthService(user).auth(salaryArchives, AuthFilterTypeEnum.QUERY_DATA, SalaryArchiveListDTO.class);


        List<Map<String, Object>> listMaps = buildSalaryArchiveData(salaryArchives, taxAgentList, salaryItems, Boolean.FALSE);
        // 组装数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);
        listMaps.forEach(e -> {
            List<Object> row = new ArrayList<>();
            row.add(Util.null2String(e.get("username")));
            row.add(Util.null2String(e.get("taxAgentName")));
            row.add(Util.null2String(e.get("departmentName")));
            row.add(Util.null2String(e.get("workcode")));
            row.add(Util.null2String(e.get("mobile")));
            row.add(Util.null2String(e.get("idNo")));
            row.add(Util.null2String(e.get("employeeStatus")));
            row.add(Util.null2String(e.get("payStartDate")));
            row.add(Util.null2String(e.get("payEndDate")));
            // 薪资项目数据
            for (SalaryItemPO salaryItem : salaryItems) {
                try {
                    if (salaryItem.getDataType().equals(SalaryDataTypeEnum.NUMBER.getValue())) {
                        row.add(new BigDecimal(e.containsKey(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX) ? (e.get(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX) == null ? "" : e.get(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX).toString()) : ""));
                    } else {
                        row.add(e.containsKey(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX) ? (e.get(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX) == null ? "" : e.get(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX).toString()) : "");
                    }
                } catch (Exception ex) {
                    row.add(e.containsKey(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX) ? (e.get(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX) == null ? "" : e.get(salaryItem.getId() + SalaryItemConstant.DYNAMIC_SUFFIX).toString()) : "");
                }
            }
            rows.add(row);
        });
        // 3.表数据

//        return ExcelUtil.genWorkbookV2(rows, sheetName);

        // 记录日志
        String statusStr = StringUtils.join(queryParam.getRunStatusList().stream().map(status -> SalaryArchiveStatusEnum.parseByValue(status).getDefaultLabel()).collect(Collectors.toList()), ",");
        String name = SalaryI18nUtil.getI18nLabel(0, "导出");
        LoggerContext<AddUpSituation> loggerContext = new LoggerContext<>();
        loggerContext.setTargetName(SalaryI18nUtil.getI18nLabel(0, "薪资档案 ") + statusStr);
        loggerContext.setOperateType(OperateTypeEnum.EXCEL_EXPORT.getValue());
        loggerContext.setOperateTypeName(name);
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "薪资档案") + "-" + name);
        loggerContext.setUser(user);
        SalaryElogConfig.salaryArchiveLoggerTemplate.write(loggerContext);
        return ExcelUtilPlus.genWorkbookV2WithPattern(rows, sheetName, false);
    }


    @Override
    public String savePaySet(SalaryArchiveSetPaySaveParam saveParam) {
        long currentEmployeeId = user.getUID();
        if (saveParam.getStatus() == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109201, "状态不能为空"));
        }
        Boolean enableStatus = saveParam.getStatus().equals(SalaryArchiveStatusEnum.PENDING)
                || saveParam.getStatus().equals(SalaryArchiveStatusEnum.FIXED)
                || saveParam.getStatus().equals(SalaryArchiveStatusEnum.SUSPEND);
        if (!enableStatus) {
            return StringUtils.EMPTY;
        }
        if (saveParam.getSalaryArchiveId() == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(100425, "薪资档案id不能为空"));
        }
        SalaryArchivePO salaryArchive = getById(saveParam.getSalaryArchiveId());
        if (salaryArchive == null) {
            throw new SalaryRunTimeException(String.format(SalaryI18nUtil.getI18nLabel(100463, "薪资档案不存在") + "[id:%s]", saveParam.getSalaryArchiveId()));
        }
        SalaryArchivePO oldSalaryArchive = new SalaryArchivePO();
        BeanUtils.copyProperties(salaryArchive, oldSalaryArchive);
        List<SalaryArchivePO> oldList = Collections.singletonList(oldSalaryArchive);
        // 收入所得项目
//        boolean checkIncomeCategory = saveParam.getIncomeCategory() == null || StringUtils.isEmpty(SalaryArchiveBO.getIncomeCategoryByValue(saveParam.getIncomeCategory()));
//        if (checkIncomeCategory) {
//            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(122085, "收入所得项目不能为空或不存在"));
//        } else {
//            salaryArchive.setIncomeCategory(saveParam.getIncomeCategory());
//        }
        // 薪资账套
//        if (CollectionUtils.isEmpty(saveParam.getSalarySobIds())) {
//            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(122217, "薪资账套不能为空"));
//        }

        // 待定薪
        if (saveParam.getStatus().equals(SalaryArchiveStatusEnum.PENDING)) {
            if (saveParam.getPayStartDate() == null) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109202, "起始发薪日期不能为空"));
            }
            // 起薪日期：新人入职默认为：入职日期;返聘人员默认为：返聘开始日期;调整个税扣缴义务人：不默认日期
            salaryArchive.setPayStartDate(saveParam.getPayStartDate());
            if (saveParam.getPayEndDate() != null) {
                if (saveParam.getPayStartDate().after(saveParam.getPayEndDate())) {
                    throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109214, "起始发薪日期不可晚于最后发薪日"));
                }
            }
            salaryArchive.setPayEndDate(saveParam.getPayEndDate());
            // 处理薪资项目
            if (CollectionUtils.isNotEmpty(saveParam.getSalaryArchiveItems())) {
                // 获取所有可被引用的薪资项目
                List<SalaryItemPO> salaryItems = getSalaryArchiveItemService(user).getCanAdjustSalaryItems();
                Collection<Long> salaryItemIds = salaryItems.stream().map(SalaryItemPO::getId).collect(Collectors.toList());
                // 待处理薪资项目
                List<SalaryArchiveItemDetailSaveParam> salaryArchiveItems = saveParam.getSalaryArchiveItems().stream().filter(e -> salaryItemIds.contains(e.getSalaryItemId())).collect(Collectors.toList());
                List<Long> salaryItemExistIds = salaryArchiveItems.stream().map(SalaryArchiveItemDetailSaveParam::getSalaryItemId).collect(Collectors.toList());
                // 获取薪资档案所对应的当前生效的薪资项目数据
                List<SalaryArchiveItemPO> salaryArchiveItemCurrentList = getSalaryArchiveItemService(user).getCurrentEffectiveItemList(Collections.singletonList(saveParam.getSalaryArchiveId()), salaryItemExistIds);
                List<SalaryArchiveItemPO> salaryArchiveItemAddTodos = Lists.newArrayList();
                List<Long> salaryArchiveItemDelTodoIds = Lists.newArrayList();
                Date now = new Date();
                salaryArchiveItems.forEach(f -> {
                    Optional<SalaryItemPO> optional = salaryItems.stream().filter(i -> i.getId().equals(f.getSalaryItemId())).findFirst();
                    if (optional.isPresent()) {
                        SalaryItemPO salaryItemPO = optional.get();
                        boolean isNotNumber = salaryItemPO.getDataType().equals(SalaryArchiveFieldTypeEnum.NUMBER.getValue()) && StringUtils.isNotEmpty(f.getAdjustValue()) && !Pattern.matches(SalaryEntityUtil.NUMBER_REGEX, f.getAdjustValue());
                        if (isNotNumber) {
                            throw new SalaryRunTimeException(salaryItemPO.getName() + ":" + SalaryI18nUtil.getI18nLabel(100581, "请输入数字"));
                        }
                    }
                    Optional<SalaryArchiveItemPO> optionalSAI = salaryArchiveItemCurrentList.stream().filter(c ->
                            c.getSalaryArchiveId().equals(saveParam.getSalaryArchiveId()) && c.getSalaryItemId().equals(f.getSalaryItemId())
                    ).findFirst();
                    if (StringUtils.isNotEmpty(f.getAdjustValue())) {
                        salaryArchiveItemAddTodos.add(SalaryArchiveItemPO.builder()
                                .id(IdGenerator.generate())
                                .salaryArchiveId(salaryArchive.getId())
                                .employeeId(salaryArchive.getEmployeeId())
                                .effectiveTime(saveParam.getPayStartDate())
                                .adjustReason(SalaryArchiveItemAdjustReasonEnum.INIT.getValue())
                                .salaryItemId(f.getSalaryItemId())
                                .itemValue(f.getAdjustValue())
                                .description("")
                                .operator(currentEmployeeId)
                                .operateTime(now)
                                .createTime(now)
                                .updateTime(now)
                                .creator(currentEmployeeId)
                                .deleteType(NumberUtils.INTEGER_ZERO)
                                .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                                .build());
                    }
                    optionalSAI.ifPresent(salaryArchiveItemPO -> salaryArchiveItemDelTodoIds.add(salaryArchiveItemPO.getId()));
                });
                // 落库处理薪资档案项目数据
                if (CollectionUtils.isNotEmpty(salaryArchiveItemAddTodos)) {
                    salaryArchiveItemMapper.batchInsert(salaryArchiveItemAddTodos, user);
                }
                if (CollectionUtils.isNotEmpty(salaryArchiveItemDelTodoIds)) {
                    salaryArchiveItemMapper.deleteBatchIds(salaryArchiveItemDelTodoIds);
                }
            }
            // 定薪
        } else if (saveParam.getStatus().equals(SalaryArchiveStatusEnum.FIXED)) {
            if (saveParam.getPayStartDate() == null) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109202, "起始发薪日期不能为空"));
            }
            salaryArchive.setPayStartDate(saveParam.getPayStartDate());
            if (saveParam.getPayEndDate() != null) {
                if (saveParam.getPayStartDate().after(saveParam.getPayEndDate())) {
                    throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109214, "起始发薪日期不可晚于最后发薪日"));
                }
            }
            salaryArchive.setPayEndDate(saveParam.getPayEndDate());
            //如果是非系统人员，需要将起始发薪日期初始化
            if (Objects.equals(salaryArchive.getEmployeeType(), 1)) {
                salaryArchive.setRunStatus(SalaryArchiveStatusEnum.FIXED.getValue());
            }
            // 待停薪
        } else if (saveParam.getStatus().equals(SalaryArchiveStatusEnum.SUSPEND)) {
            if (saveParam.getPayEndDate() == null) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109203, "最后发薪日期不能为空"));
            }
            if (saveParam.getPayStartDate().after(saveParam.getPayEndDate())) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109214, "起始发薪日期不可晚于最后发薪日"));
            }
            salaryArchive.setPayEndDate(saveParam.getPayEndDate());
        }
        salaryArchive.setUpdateTime(new Date());
        getSalaryArchiveMapper().update(salaryArchive);

//        List<SalarySobPO> salarySobList = getSalarySobList(currentEmployeeId, currentTenantKey);
//        List<Long> salarySobIds = salarySobList.stream().map(SalarySobPO::getId).filter(id -> saveParam.getSalarySobIds().contains(id)).collect(Collectors.toList());
//        List<SalaryArchiveSobPO> salaryArchiveSobSaveList = SalaryArchiveBO.buildSalaryArchiveSob(salaryArchive.getId(), salarySobIds, LocalDateTime.now());
//        this.salaryArchiveSobService.saveBatchBySalaryArchiveIdsAndSaves(Collections.singletonList(salaryArchive.getId()), salaryArchiveSobSaveList, currentTenantKey);

        // 记录日志
        List<SalaryArchivePO> newList = Collections.singletonList(this.getById(salaryArchive.getId()));
        String operatedesc = SalaryI18nUtil.getI18nLabel(0, "发薪设置");
        recordLog(oldList, newList, operatedesc);

        return StringUtils.EMPTY;
    }

    /**
     * 记录日志
     *
     * @param oldSalaryArchiveList
     * @param newSalaryArchiveList
     * @param operatedesc
     */
    private void recordLog(List<SalaryArchivePO> oldSalaryArchiveList, List<SalaryArchivePO> newSalaryArchiveList, String operatedesc) {
        if (CollectionUtils.isEmpty(oldSalaryArchiveList) || CollectionUtils.isEmpty(oldSalaryArchiveList)) {
            return;
        }
        List<Long> updateEmpIds = newSalaryArchiveList.stream().map(SalaryArchivePO::getEmployeeId).distinct().collect(Collectors.toList());
        List<DataCollectionEmployee> employeeList = getSalaryEmployeeService(user).listByIds(updateEmpIds);
        Map<Long, String> empMap = SalaryEntityUtil.convert2Map(employeeList, DataCollectionEmployee::getEmployeeId, DataCollectionEmployee::getUsername);


        // 获取所有个税扣缴义务人
        Collection<TaxAgentPO> taxAgents = getTaxAgentService(user).listAll();
        Map<Long, String> taxAgentMap = SalaryEntityUtil.convert2Map(taxAgents, TaxAgentPO::getId, TaxAgentPO::getName);
        String bar = "-";
        oldSalaryArchiveList.forEach(e -> {
            LoggerContext loggerContext = new LoggerContext();
            loggerContext.setUser(user);
            loggerContext.setTargetId(String.valueOf(e.getId()));
            loggerContext.setTargetName( Optional.ofNullable(taxAgentMap.get(e.getTaxAgentId())).orElse(StringUtils.EMPTY) + bar + Optional.ofNullable(empMap.get(e.getEmployeeId())).orElse(StringUtils.EMPTY) );
            loggerContext.setOperateType(OperateTypeEnum.UPDATE.getValue());
            loggerContext.setOperateTypeName(operatedesc);
            loggerContext.setOperatedesc(operatedesc);
            loggerContext.setOldValues(e);
            Optional<SalaryArchivePO> optionalNew = newSalaryArchiveList.stream().filter(n -> n.getId().equals(e.getId())).findFirst();
            loggerContext.setNewValues(optionalNew.isPresent() ? optionalNew.get() : null);
            SalaryElogConfig.salaryArchiveLoggerTemplate.write(loggerContext);
        });
    }


    @Override
    public List<SalaryArchiveDimissionPO> dimissionSets() {
        return salaryArchiveDimissionMapper.dimissionSets();
    }

    @Override
    public String saveDimissionSet(SalaryArchiveDimissionSaveParam saveParam) {
        Date now = new Date();
        List<SalaryArchiveDimissionPO> dimissionSets = dimissionSets();
        if (CollectionUtils.isNotEmpty(dimissionSets)) {
            SalaryArchiveDimissionPO po = dimissionSets.get(0);
            po.setDimissionTimeInterval(saveParam.getDimissionTimeInterval().getValue());
            po.setUpdateTime(now);
            salaryArchiveDimissionMapper.updateById(po);
        } else {
            salaryArchiveDimissionMapper.insert(SalaryArchiveDimissionPO.builder().dimissionTimeInterval(saveParam.getDimissionTimeInterval().getValue()).createTime(now).updateTime(now).creator((long) user.getUID()).tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY).build());
        }
        return StringUtils.EMPTY;
    }

    @Override
    public List<SalaryArchiveDataDTO> getSalaryArchiveData(LocalDateRange localDateRange, Collection<Long> employeeIds, List<Long> taxAgentIds) {
        return getSalaryArchiveData(localDateRange, employeeIds, taxAgentIds, Boolean.FALSE);
    }

    @Override
    public List<SalaryArchiveDataDTO> getSalaryArchiveTaxAgentData(LocalDateRange localDateRange, Collection<Long> employeeIds, List<Long> taxAgentIds) {
        return getSalaryArchiveData(localDateRange, employeeIds, taxAgentIds, Boolean.TRUE);
    }

    /**
     * 根据日期范围和人员获取薪资档案数据
     *
     * @param localDateRange
     * @param employeeIds
     * @param isOnlyTaxAgent
     * @return
     */
    private List<SalaryArchiveDataDTO> getSalaryArchiveData(LocalDateRange localDateRange, Collection<Long> employeeIds, List<Long> taxAgentIds, boolean isOnlyTaxAgent) {
        // 获取核算人员规则
        List<String> statusList = Collections.emptyList();
        SalarySysConfPO employeeRule = getSalarySysConfService(user).getOneByCode(SalarySysConstant.SALARY_ACCT_EMPLOYEE_RULE);
        if (Objects.isNull(employeeRule) || StringUtils.equals(employeeRule.getConfValue(), SalaryAcctEmployeeRuleEnum.BYPAYENDTIME.getValue())) {
            // 默认包含停薪列表
            statusList = Arrays.asList(SalaryArchiveStatusEnum.FIXED.getValue(), SalaryArchiveStatusEnum.SUSPEND.getValue(),
                    SalaryArchiveStatusEnum.STOP_FROM_SUSPEND.getValue());
        } else {
            // 仅包含发薪、待定薪
            statusList = Arrays.asList(SalaryArchiveStatusEnum.FIXED.getValue(), SalaryArchiveStatusEnum.SUSPEND.getValue());
        }
        // 获取薪资档案数据
        List<SalaryArchivePO> salaryArchiveList = listSome(SalaryArchivePO.builder().runStatusList(statusList).employeeIds(employeeIds).taxAgentIds(taxAgentIds).build());

        List<Long> allEmployeeIds = salaryArchiveList.stream().map(SalaryArchivePO::getEmployeeId).distinct().collect(Collectors.toList());
        // 获取所有可被引用的薪资项目
        List<SalaryItemPO> salaryItems = salaryItemMapper.getCanAdjustSalaryItems();
        Collection<Long> salaryItemIds = salaryItems.stream().map(SalaryItemPO::getId).collect(Collectors.toList());
        // 获取薪资项目调整数据,isOnlyTaxAgent为true时不需要薪资项目数据
        List<SalaryArchiveItemPO> salaryArchiveItemDataList = isOnlyTaxAgent || CollectionUtils.isEmpty(salaryItemIds) ? Lists.newArrayList() : salaryArchiveItemMapper.listByParam(SalaryArchiveItemQueryParam.builder().salaryItemIds(salaryItemIds).employeeIds(employeeIds).effectiveTime(localDateRange.getEndDate()).build());

        return SalaryArchiveBO.buildSalaryArchiveData(salaryArchiveList, salaryArchiveItemDataList, localDateRange, allEmployeeIds, isOnlyTaxAgent);
    }


    @Override
    public void stopSalary(SalaryArchiveStopParam stopSalaryParam) {
        ValidUtil.doValidator(stopSalaryParam);
        stopSalaryParam.setPayEndDate(SalaryDateUtil.stringToDate(stopSalaryParam.getPayEndDateStr()));

        SalaryArchivePO po = getSalaryArchiveMapper().getById(stopSalaryParam.getSalaryArchiveId());

        if (po == null) {
            throw new SalaryRunTimeException("未找到薪资档案");
        }
        Date payStartDate = po.getPayStartDate();
        Date payEndDate = stopSalaryParam.getPayEndDate();
        if (payEndDate.before(payStartDate)) {
            throw new SalaryRunTimeException("最后缴纳日期应大于起始缴纳日期");
        }

        po.setPayEndDate(payEndDate);
        po.setUpdateTime(new Date());
        getSalaryArchiveMapper().update(po);

    }


    @Override
    public Map<String, Object> preview(SalaryArchiveImportHandleParam param) {
        //1、参数校验
        vaildImportParam(param);

        Map<String, Object> map = new HashMap<>();

        InputStream fileInputStream = null;
        try {

            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(param.getImageId()));

            Sheet sheet = ExcelSupport.parseFile(fileInputStream, 0, EXCEL_TYPE_XLSX);
            List<String> headers = ExcelSupport.getSheetHeader(sheet, 0);

            map.put("headers", headers);
            map.put("list", ExcelParseHelper.parse2List(sheet, 1));

            return map;

        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    private void vaildImportParam(SalaryArchiveImportHandleParam param) {
        String imageId = param.getImageId();
        if (StringUtils.isBlank(imageId)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(100593, "文件id为空"));
        }
        if (param.getListType() == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109712, "列表类型必传"));
        } else {
            // 定薪列表导入有调薪导入和初始化导入
            if (param.getListType().equals(SalaryArchiveListTypeEnum.FIXED.getValue())) {
                Optional<SalaryArchiveImportTypeEnum> optional = Arrays.stream(SalaryArchiveImportTypeEnum.values())
                        .filter(e -> StringUtils.isNotEmpty(param.getImportType()) && e.getValue().equals(param.getImportType())).findFirst();
                if (!optional.isPresent()) {
                    throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(100593, "导入类型不正确"));
                }
            }
        }
    }

    private List<SalaryArchiveTaxAgentPO> getEffectiveTaxAgentList() {
        Date truncate = DateUtils.truncate(new Date(), Calendar.DATE);
        return salaryArchiveTaxAgentMapper.getEffectiveTaxAgentList(SalaryArchiveTaxAgentQueryParam.builder().effectiveTime(truncate).build());
    }

    private List<SalaryArchiveTaxAgentPO> getIneffectiveTaxAgentList() {
        Date truncate = DateUtils.truncate(new Date(), Calendar.DATE);
        return salaryArchiveTaxAgentMapper.getIneffectiveTaxAgentList(SalaryArchiveTaxAgentQueryParam.builder().effectiveTime(truncate).build());
    }


    /**
     * 处理历史数据
     * 下回发版可删除
     */
    public void handleHistory(Long currentEmployeeId) {
        // 待定薪没设置过的默认为正常工资薪金所得
//        new LambdaUpdateChainWrapper<>(salaryArchiveMapper)
//                .eq(SalaryArchivePO::getDeleteType, 0)
//                .eq(SalaryArchivePO::getTenantKey, tenantKey)
//                .eq(SalaryArchivePO::getRunStatus, SalaryArchiveStatusEnum.PENDING.getValue())
//                .isNull(SalaryArchivePO::getIncomeCategory)
//                .set(SalaryArchivePO::getIncomeCategory, IncomeCategoryEnum.WAGES_AND_SALARIES.getValue())
//                .update();

        //历史档案默认为发薪员工
        List<SalaryArchivePO> historyData = getSalaryArchiveMapper().getStatusIsNullData();
        List<Long> ids = SalaryEntityUtil.properties(historyData, SalaryArchivePO::getId, Collectors.toList());
        List<List<Long>> partIds = Lists.partition(ids, 999);
        for (List<Long> part : partIds) {
            getSalaryArchiveMapper().updateStatus(SalaryArchivePO.builder().ids(part).runStatus(SalaryArchiveStatusEnum.FIXED.getValue()).build());
        }

        //处理个税扣缴义务人为空
        List<SalaryArchivePO> salaryArchives = getSalaryArchiveMapper().getHistoryData();
        if (CollectionUtils.isEmpty(salaryArchives)) {
            return;
        }
        Date today = new Date();
        List<SalaryArchiveTaxAgentPO> salaryArchiveTaxAgents = getEffectiveTaxAgentList();

        List<SalaryArchiveItemPO> salaryArchiveItems = salaryArchiveItemMapper.getEffectiveSalaryItems(SalaryArchiveItemQueryParam.builder().effectiveTime(today).build());

        List<Long> salaryArchiveDelIds = salaryArchives.stream().map(SalaryArchivePO::getId).collect(Collectors.toList());
        List<Long> salaryArchiveTaxAgentDelIds = salaryArchiveTaxAgents.stream().map(SalaryArchiveTaxAgentPO::getId).collect(Collectors.toList());
        List<Long> salaryArchiveItemDelIds = salaryArchiveItems.stream().map(SalaryArchiveItemPO::getId).collect(Collectors.toList());

        List<SalaryArchivePO> salaryArchiveAddList = Lists.newArrayList();
        List<SalaryArchiveItemPO> salaryArchiveItemAddList = Lists.newArrayList();


        Date endDate = today;
        for (SalaryArchivePO sa : salaryArchives) {
            List<SalaryArchiveTaxAgentPO> saTaxAgents = salaryArchiveTaxAgents.stream().filter(t -> t.getSalaryArchiveId().equals(sa.getId())).collect(Collectors.toList());
            // 去重获取最新生效的个税口角义务人列表
            saTaxAgents = saTaxAgents.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(f -> f.getSalaryArchiveId() + "-" + f.getTaxAgentId()))), ArrayList::new));

            List<SalaryArchiveItemPO> saItemList = salaryArchiveItems.stream().filter(i -> i.getSalaryArchiveId().equals(sa.getId())).collect(Collectors.toList());

            for (SalaryArchiveTaxAgentPO st : saTaxAgents) {
                Date finalEndDate = endDate;
                List<SalaryArchiveItemPO> saItems = saItemList.stream().filter(si -> !si.getEffectiveTime().after(finalEndDate)).collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(f -> f.getSalaryArchiveId() + "-" + f.getSalaryItemId()))), ArrayList::new));

                SalaryArchivePO newSA = SalaryArchivePO.builder().id(IdGenerator.generate()).employeeId(sa.getEmployeeId()).taxAgentId(st.getTaxAgentId()).payStartDate(st.getEffectiveTime())
//                        .runStatus(SalaryArchiveStatusEnum.FIXED.getValue())
//                        .modifier(0L)
                        .createTime(today).updateTime(today).creator(currentEmployeeId).deleteType(NumberUtils.INTEGER_ZERO).tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY).build();
                salaryArchiveAddList.add(newSA);
                saItems.forEach(sf -> {
                    salaryArchiveItemAddList.add(SalaryArchiveItemPO.builder().salaryArchiveId(newSA.getId()).employeeId(newSA.getEmployeeId()).effectiveTime(sf.getEffectiveTime()).adjustReason(sf.getAdjustReason()).salaryItemId(sf.getSalaryItemId()).itemValue(sf.getItemValue()).description(sf.getDescription())
                            // 不用设置操作人
                            .operator(currentEmployeeId).operateTime(today).createTime(today).updateTime(today).creator(currentEmployeeId).deleteType(NumberUtils.INTEGER_ZERO).tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY).build());
                });

//                endDate = st.getEffectiveTime();
            }
        }

        // 逻辑删除历史数据
        List<List<Long>> partition = Lists.partition(salaryArchiveDelIds, 999);
        for (List<Long> part : partition) {
            salaryArchiveMapper.batchDeleteHistoryData(SalaryArchivePO.builder().ids(part).build());
        }
        List<List<Long>> partitionTA = Lists.partition(salaryArchiveTaxAgentDelIds, 999);
        for (List<Long> part : partitionTA) {
            salaryArchiveTaxAgentMapper.batchDeleteHistoryData(SalaryArchiveTaxAgentPO.builder().ids(part).build());
        }
        List<List<Long>> partitionItem = Lists.partition(salaryArchiveItemDelIds, 999);
        for (List<Long> part : partitionItem) {
            salaryArchiveItemMapper.batchDeleteHistoryData(SalaryArchiveItemPO.builder().ids(part).build());
        }
        // 新增迁移数据
        if (CollectionUtils.isNotEmpty(salaryArchiveAddList)) {
            this.salaryArchiveMapper.batchInsert(salaryArchiveAddList);
        }
        if (CollectionUtils.isNotEmpty(salaryArchiveItemAddList)) {
            this.salaryArchiveItemMapper.batchInsert(salaryArchiveItemAddList, user);
        }
        log.info("处理历史数据结束===============");
    }

    @Override
    public Map<String, Object> selectSalaryArchiveColumns(SalaryArchiveImportTypeEnum salaryArchiveImportTypeEnum) {
        HashMap<String, Object> map = Maps.newHashMap();
        //类型为档案初始化
        boolean isInit = salaryArchiveImportTypeEnum.getValue().equals(SalaryArchiveImportTypeEnum.INIT.getValue());
        //类型为调整个税扣缴义务人
        boolean isTaxAgentAdjust = salaryArchiveImportTypeEnum.getValue().equals(SalaryArchiveImportTypeEnum.TAXAGENTADJUST.getValue());
        //类型为调薪
        boolean isSalaryItemAdjust = salaryArchiveImportTypeEnum.getValue().equals(SalaryArchiveImportTypeEnum.SALARYITEMADJUST.getValue());

        //固定列
        map.put(SalaryI18nUtil.getI18nLabel(85429, "姓名"), "username");
        map.put(SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"), "taxAgentName");
        map.put(SalaryI18nUtil.getI18nLabel(85904, "生效日期"), "taxAgentEffectiveTime");
        map.put(SalaryI18nUtil.getI18nLabel(86185, "部门"), "departmentName");
        map.put(SalaryI18nUtil.getI18nLabel(86186, "手机号"), "mobile");
        map.put(SalaryI18nUtil.getI18nLabel(91075, "员工状态"), "employeeStatus");

        List<SalaryItemPO> salaryItems = salaryItemMapper.getCanAdjustSalaryItems();

        if (isTaxAgentAdjust || isSalaryItemAdjust) {
            map.put(SalaryI18nUtil.getI18nLabel(85431, "调整原因"), "adjustReason");
        }
        //动态列
        // 获取所有可被引用的薪资项目
        if (isInit || isSalaryItemAdjust) {
            for (SalaryItemPO salaryItem : salaryItems) {
                map.put(salaryItem.getName(), salaryItem.getCode());
            }
        }

        return map;
    }

    @Override
    public Map<String, Long> queryTabTotal() {
        // tab页签数量
        Map<String, Long> result = new HashMap<>();

        List<SalaryArchivePO> list = getSalaryArchiveMapper().listAll();

        list = getAuthService(user).auth(list, AuthFilterTypeEnum.QUERY_DATA, SalaryArchivePO.class);

        long pendingTotal = 0L;
        long fixedTotal = 0L;
        long suspendTotal = 0L;
        long stopTotal = 0L;
        long extTotal = 0L;

        for (SalaryArchivePO sa : list) {
            Integer employeeType = sa.getEmployeeType();
            if (employeeType == null || employeeType == 0) {
                if (sa.getRunStatus().equals(SalaryArchiveStatusEnum.PENDING.getValue())) {
                    pendingTotal += 1;
                } else if (sa.getRunStatus().equals(SalaryArchiveStatusEnum.FIXED.getValue()) || sa.getRunStatus().equals(SalaryArchiveStatusEnum.SUSPEND.getValue())) {
                    fixedTotal += 1;
                    if (sa.getRunStatus().equals(SalaryArchiveStatusEnum.SUSPEND.getValue())) {
                        suspendTotal += 1;
                    }
                } else if (sa.getRunStatus().equals(SalaryArchiveStatusEnum.STOP_FROM_PENDING.getValue()) || sa.getRunStatus().equals(SalaryArchiveStatusEnum.STOP_FROM_SUSPEND.getValue())) {
                    stopTotal += 1;
                }
            } else {
                extTotal += 1;
            }
        }

        result.put(SalaryArchiveListTypeEnum.PENDING.getValue(), pendingTotal);
        result.put(SalaryArchiveListTypeEnum.FIXED.getValue(), fixedTotal);
        result.put(SalaryArchiveListTypeEnum.SUSPEND.getValue(), suspendTotal);
        result.put(SalaryArchiveListTypeEnum.STOP.getValue(), stopTotal);
        result.put(SalaryArchiveListTypeEnum.EXT.getValue(), extTotal);
        return result;
    }

    @Override
    public String deletePendingTodo(Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "参数错误"));
        }

        List<SalaryArchivePO> oldList = listSome(SalaryArchivePO.builder()
                .ids(ids)
                .runStatus(SalaryArchiveStatusEnum.PENDING.getValue())
                .build());


        if (CollectionUtils.isEmpty(oldList)) {
            return StringUtils.EMPTY;
        }
        // 从待定薪到停薪
        List<List<Long>> partition = Lists.partition((List<Long>) ids, 800);
        partition.forEach(getSalaryArchiveMapper()::deletePendingTodo);
        // 记录日志
        List<SalaryArchivePO> newList = new ArrayList<>();
        partition.forEach(part -> {
            newList.addAll(getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().ids(part).build()));
        });
        String operatedesc = SalaryI18nUtil.getI18nLabel(0, "删除待办");
        recordLog(oldList, newList, operatedesc);

        return StringUtils.EMPTY;
    }


    @Override
    public Map<String, Object> gotoFixed(Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(145969, "没有可以操作的记录"));
        }

        List<SalaryArchivePO> salaryArchiveList = listSome(SalaryArchivePO.builder().ids(ids).runStatus(SalaryArchiveStatusEnum.PENDING.getValue()).build());

        if (CollectionUtils.isEmpty(salaryArchiveList)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(145969, "档案不存在！"));
        }

        List<SalaryArchivePO> unableList = salaryArchiveList.stream().filter(f -> f.getPayStartDate() == null).collect(Collectors.toList());

        int total = ids.size();
        int errorCount = unableList.size();
        // 单个设为定薪的失败提示
        if (total == 1 && errorCount > 0) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109197, "请先维护起始发薪日期"));
        }

        // 从待定薪到定薪
        List<List<Long>> partition = Lists.partition((List<Long>) ids, 800);
        partition.forEach(getSalaryArchiveMapper()::gotoFixed);

        // 记录日志
        List<SalaryArchivePO> newList = new ArrayList<>();
        partition.forEach(part -> {
            newList.addAll(getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().ids(part).build()));
        });
        String operateDesc = SalaryI18nUtil.getI18nLabel(0, "设为发薪员工");
        recordLog(salaryArchiveList, newList, operateDesc);

        // 获取所有可被引用的薪资项目
        List<Long> salaryItemIds = getSalaryArchiveItemService(user).getCanAdjustSalaryItems().stream().map(SalaryItemPO::getId).collect(Collectors.toList());
        List<SalaryArchiveItemPO> currentEffectiveItemList = getSalaryArchiveItemService(user).getCurrentEffectiveItemListIngoreValue(ids, salaryItemIds)
                .stream()
                .peek(e -> {
                    Optional<SalaryArchivePO> salaryArchive = salaryArchiveList.stream().filter(sa -> sa.getId().equals(e.getSalaryArchiveId())).findFirst();
                    if (salaryArchive.isPresent()) {
                        e.setAdjustReason(SalaryArchiveItemAdjustReasonEnum.INIT.getValue());
                        e.setEffectiveTime(salaryArchive.get().getPayStartDate());
                    }
                }).collect(Collectors.toList());
        // 更新薪资项目的生效日期
        List<List<SalaryArchiveItemPO>> partitions = Lists.partition(currentEffectiveItemList, 100);
        partitions.forEach(list -> getSalaryArchiveItemMapper().batchUpdate(list));

        Map<String, Object> resultMap = new HashMap<>(2);
        String resultMsg = SalaryI18nUtil.getI18nLabel(94620, "操作成功");
        String resultType = "success";
        // 批量设为定薪提示
        if (total > 1) {
            resultMsg = SalaryI18nUtil.getI18nLabel(134807, "成功条数") + ": " + (total - errorCount) + "; "
                    + SalaryI18nUtil.getI18nLabel(134808, "失败条数") + ": " + (errorCount)
                    + (errorCount > 0 ? ", " + SalaryI18nUtil.getI18nLabel(109197, "请先维护起始发薪日期") : "");
            resultType = errorCount > 0 ? "info" : resultType;
        }
        resultMap.put("type", resultType);
        resultMap.put("msg", resultMsg);
        return resultMap;
    }

    @Override
    public Map<String, Object> allGotoFixed(SalaryArchiveQueryParam queryParam) {
        queryParam.setRunStatusList(Arrays.asList(SalaryArchiveStatusEnum.PENDING.getValue()));
        queryParam.setFilterType(AuthFilterTypeEnum.ADMIN_DATA);
        List<Long> salaryArchiveIds = this.list(queryParam).stream().map(SalaryArchiveListDTO::getId).collect(Collectors.toList());
        return this.gotoFixed(salaryArchiveIds);
    }


    @Override
    public Map<String, Object> gotoStop(Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(145969, "没有可以操作的记录"));
        }
        List<SalaryArchivePO> oldList = listSome(SalaryArchivePO.builder()
                .ids(ids)
                .runStatus(SalaryArchiveStatusEnum.SUSPEND.getValue())
                .build());

        if (CollectionUtils.isEmpty(oldList)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(145969, "档案不存在！"));
        }

        List<SalaryArchivePO> unableList = oldList.stream().filter(f -> f.getPayEndDate() == null).collect(Collectors.toList());
        int total = ids.size();
        int errorCount = unableList.size();
        // 单个设为定薪的失败提示
        if (total == 1 && errorCount > 0) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(109196, "请先维护最后发薪日期"));
        }

        // 从待停薪到停薪
        List<List<Long>> partition = Lists.partition((List<Long>) ids, 800);
        partition.forEach(getSalaryArchiveMapper()::gotoStop);

        // 记录日志
        List<SalaryArchivePO> newList = new ArrayList<>();
        partition.forEach(part -> {
            newList.addAll( getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().ids(part).build()));
        });
        String operatedesc = SalaryI18nUtil.getI18nLabel(0, "停薪");
        recordLog(oldList, newList, operatedesc);

        Map<String, Object> resultMap = new HashMap<>(2);
        String resultMsg = SalaryI18nUtil.getI18nLabel(94620, "操作成功");
        String resultType = "success";
        // 批量设为定薪提示
        if (total > 1) {
            resultMsg = SalaryI18nUtil.getI18nLabel(134807, "成功条数") + ": " + (total - errorCount) + "; "
                    + SalaryI18nUtil.getI18nLabel(134808, "失败条数") + ": " + (errorCount)
                    + (errorCount > 0 ? ", " + SalaryI18nUtil.getI18nLabel(109196, "请先维护最后发薪日期") : "");
            resultType = errorCount > 0 ? "info" : resultType;
        }
        resultMap.put("type", resultType);
        resultMap.put("msg", resultMsg);
        return resultMap;
    }

    @Override
    public Map<String, Object> allGotoStop(SalaryArchiveQueryParam queryParam) {
        queryParam.setRunStatusList(Arrays.asList(SalaryArchiveStatusEnum.SUSPEND.getValue()));
        queryParam.setFilterType(AuthFilterTypeEnum.ADMIN_DATA);
        List<Long> salaryArchiveIds = this.list(queryParam)
                .stream().map(SalaryArchiveListDTO::getId).collect(Collectors.toList());
        return this.gotoStop(salaryArchiveIds);
    }

    @Override
    public String deleteSuspendTodo(Collection<Long> ids) {
        if (org.springframework.util.CollectionUtils.isEmpty(ids)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "参数错误"));
        }

//        List<SalaryArchivePO> oldList = listSome(SalaryArchivePO.builder()
//                .ids(ids)
//                .runStatus(SalaryArchiveStatusEnum.SUSPEND.getValue())
//                .build());
//        List<SalaryArchivePO> unableList = oldList.stream().filter(f -> Objects.nonNull(f.getPayEndDate()) && !f.getPayEndDate().after(new Date())).collect(Collectors.toList());
//        if (CollectionUtils.isNotEmpty(unableList)) {
//            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(115789, "最后发薪日必须晚于今天"));
//        }
//
//        // 从待停薪到定薪
//        getSalaryArchiveMapper().deleteSuspendTodo(ids);
//        return StringUtils.EMPTY;

        // 获取所有个税扣缴义务人
//        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree(Long.valueOf(user.getUID()));
        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree();
        // 获取删除待停薪信息
        List<SalaryArchiveListDTO> list = getSalaryArchiveMapper().list(SalaryArchiveQueryParam.builder().ids(ids).build());
        if (list.size() != ids.size()) {
            throw new SalaryRunTimeException("薪资档案不存在，删除失败！");
        }
        Set<String> employeeStatus = list.stream().map(SalaryArchiveListDTO::getEmployeeStatus).collect(Collectors.toSet());
        if (employeeStatus.contains("5")) {
            throw new SalaryRunTimeException("离职人员无法删除待办");
        }
        Map<Long, TaxAgentManageRangeEmployeeDTO> taxAgentMap = SalaryEntityUtil.convert2Map(taxAgentList, TaxAgentManageRangeEmployeeDTO::getTaxAgentId);
        // 判断删除待停薪代办的人是否在个税扣缴义务人的人员范围中
        for (SalaryArchiveListDTO dto : list) {
            TaxAgentManageRangeEmployeeDTO taxAgentManageRangeEmployeeDTO = taxAgentMap.get(dto.getTaxAgentId());
            if (Objects.nonNull(taxAgentManageRangeEmployeeDTO)) {
                boolean canDelete = taxAgentManageRangeEmployeeDTO.getEmployeeList().stream()
                        .map(TaxAgentManageRangeEmployeeDTO.TaxAgentEmployee::getEmployeeId)
                        .anyMatch(id -> Objects.equals(id, dto.getEmployeeId()));
                if (!canDelete) {
                    throw new SalaryRunTimeException("个税扣缴义务人不存在或不在权限范围内，删除失败！");
                }
            } else {
                throw new SalaryRunTimeException("个税扣缴义务人不存在或不在权限范围内，删除失败！");
            }
        }
        // 删除最后发薪日期，设置状态为发薪
        if (CollectionUtils.isNotEmpty(list)) {
            List<Long> salaryArchiveIds = list.stream().map(SalaryArchiveListDTO::getId).collect(Collectors.toList());
            getSalaryArchiveMapper().deleteSuspendTodo(salaryArchiveIds);
            // 记录日志
            List<SalaryArchivePO> oldList = getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().ids(salaryArchiveIds).build());
            List<SalaryArchivePO> newList = getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().ids(ids).build());
            String operatedesc = SalaryI18nUtil.getI18nLabel(0, "删除待办");
            recordLog(oldList, newList, operatedesc);
        }
        return StringUtils.EMPTY;
    }

    @Override
    public String cancelStop(Collection<Long> ids) {
        if (org.springframework.util.CollectionUtils.isEmpty(ids)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "参数错误"));
        }

        SalaryArchivePO param = SalaryArchivePO.builder()
                .ids(ids)
                .runStatusList(Arrays.asList(SalaryArchiveStatusEnum.STOP_FROM_PENDING.getValue(), SalaryArchiveStatusEnum.STOP_FROM_SUSPEND.getValue()))
                .build();
        List<SalaryArchivePO> oldList = listSome(param);
        oldList = getAuthService(user).auth(oldList, AuthFilterTypeEnum.ADMIN_DATA, SalaryArchivePO.class);

        ids = SalaryEntityUtil.properties(oldList, SalaryArchivePO::getId);
        // 从停薪到待定薪
        getSalaryArchiveMapper().gotoPendingFromStop(ids);
        // 从停薪到定薪
        getSalaryArchiveMapper().gotoFixedFromStop(ids);

        // 记录日志
        List<SalaryArchivePO> newList = getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().ids(ids).build());
        String operatedesc = SalaryI18nUtil.getI18nLabel(0, "取消停薪");
        recordLog(oldList, newList, operatedesc);

        return StringUtils.EMPTY;
    }

    @Override
    public Map<String, Object> handleRepeatData() {
        Map<String, Object> result = new HashMap<>();

        //获取所有薪资档案
        List<SalaryArchivePO> list = getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().runStatus(SalaryArchiveStatusEnum.PENDING.getValue()).build());
        AtomicInteger num = new AtomicInteger();
        if (CollectionUtils.isNotEmpty(list)) {
            Map<String, List<SalaryArchivePO>> maps = SalaryEntityUtil.group2Map(list, k -> k.getTaxAgentId() + "-" + k.getEmployeeId());
            maps.forEach((key, pos) -> {
                if (pos.size() > 1) {
                    for (int i = 1; i < pos.size(); i++) {
                        SalaryArchivePO salaryArchivePO = pos.get(i);
                        getSalaryArchiveMapper().delete(salaryArchivePO);
                        num.getAndIncrement();
                    }
                }
            });
        }
        result.put("共处理", num.get());
        return result;
    }

    @Override
    public List<SalaryArchivePO> listPayEndDateIsNull(List<Long> employeeIds) {
        if (CollectionUtils.isEmpty(employeeIds)) {
            return Collections.emptyList();
        }
        List<List<Long>> partition = Lists.partition(employeeIds, 500);
        List<SalaryArchivePO> resultList = new ArrayList<>();
        partition.forEach(part -> {
            resultList.addAll(getSalaryArchiveMapper().listPayEndDateIsNull(part));
        });
        return resultList;
    }

    @Override
    public List<SalaryArchivePO> listByRunStatus(List<String> list) {
        return getSalaryArchiveMapper().listSome(SalaryArchivePO.builder().runStatusList(list).build());
    }

    /**
     * 同步所有档案起始发薪日期变为入职日期
     *
     * @return
     */
    @Override
    public String syncPayStartDate() {
        List<DataCollectionEmployee> employeeList = getSalaryEmployeeService(user).listAll(UseEmployeeTypeEnum.ALL);
        Map<Long, String> empPayStartDateMap = SalaryEntityUtil.convert2Map(employeeList, DataCollectionEmployee::getEmployeeId, DataCollectionEmployee::getCompanystartdate);

        List<SalaryArchivePO> list = getSalaryArchiveMapper().listAll();
        List<SalaryArchivePO> archives = list.stream()
                .peek(archive -> {
                    String payStartDate = empPayStartDateMap.getOrDefault(archive.getEmployeeId(), "");
                    if (SalaryDateUtil.checkDay(payStartDate)) {
                        archive.setPayStartDate(SalaryDateUtil.dateStrToLocalDate(payStartDate));
                    }
                })
                .collect(Collectors.toList());
        salaryArchiveMapper.batchUpdate(archives);
        return "执行完毕";
    }

    @Override
    public List<SalaryArchivePO> listPayStartDateIsNull(String runStatus) {
        if (StringUtils.isBlank(runStatus)) {
            return Collections.emptyList();
        }
        return getSalaryArchiveMapper().listPayStartDateIsNull(runStatus);
    }
}
